package com.beerandjesus.sportofkings.handicapper.dao;

import com.beerandjesus.sportofkings.handicapper.dao.aspect.TransactionBoundary;
import com.beerandjesus.sportofkings.handicapper.dao.repository.ProfileHistoryRepository;
import com.beerandjesus.sportofkings.handicapper.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * Date: Jun 8, 2012
 * Time: 12:41:51 PM
 */
@Component
public class DatabaseImpl implements Database {
    @Autowired
    private TurfSireDAO turfSireDAO;
    @Autowired
    private DirtArtificialSireDAO dirtArtificialSireDAO;
    @Autowired
    private JockeyDAO jockeyDAO;
    @Autowired
    private ProfileHistoryRepository profileHistoryRepository;
    private boolean initialized = false;

    public DatabaseImpl() {
    }

    public Sire getTurfSireByName(String name) {
        Sire byName;
        try {
            byName = getTurfSireDAO().getTurfSireByName(name);
        } catch (DAOException e) {
            throw new DatabaseException("Error getting turf sire " + name, e);
        }
        return byName;
    }

    public Sire getDirtArtificialSireByName(String name) {
        Sire byName;
        try {
            byName = getDirtArtificialSireDAO().getDirtArtificialSireByName(name);
        } catch (DAOException e) {
            throw new DatabaseException("Error getting dirt/artificial sire " + name, e);
        }
        return byName;
    }

    public JockeyRating getJockeyRatingByName(String jockeyName) {
        if (jockeyName != null) {
            jockeyName = jockeyName.toLowerCase();
        }
        JockeyRating rating;
        try {
            JockeyRatingThreshold thresh = getJockeyDAO().getActiveJockeyRatingThreshold();
            JockeyPerformance perf = getJockeyDAO().getJockeyPerformanceByName(jockeyName);
            rating = calculatJockeyRating(perf, thresh);
        } catch (DAOException e) {
            throw new DatabaseException("Error calculating jockey rating");
        }
        return rating;
    }

    private JockeyRating calculatJockeyRating(JockeyPerformance perf, JockeyRatingThreshold thresh) {
        if (perf == null) {
            return null;
        }
        BigDecimal wr = perf.getWinRatio();
        CramerJockeyRating cjr = CramerJockeyRating.OTHER;
        if (wr.compareTo(thresh.getLeading()) >= 0) {
            cjr = CramerJockeyRating.LEADING;
        } else if (wr.compareTo(thresh.getExcellent()) >= 0) {
            cjr = CramerJockeyRating.EXCELLENT;
        } else if (wr.compareTo(thresh.getGood()) >= 0) {
            cjr = CramerJockeyRating.GOOD;
        }
        JockeyRatingImpl rating = new JockeyRatingImpl();
        rating.setJockeyName(perf.getName());
        rating.setRating(cjr);
        return rating;
    }

    public void savePerformances(List<JockeyPerformance> performances) {
        JockeyPerformance jp = null;
        try {
            for (JockeyPerformance p : performances) {
                jp = p;
                getJockeyDAO().save(p);
            }
        } catch (DAOException e) {
            throw new DatabaseException("Error saving jockey performance " + jp.getName());
        }

    }

    @TransactionBoundary(datasources = Datasource.JOCKEY)
    public List<JockeyRating> getAllJockeyRatings() {
        ArrayList<JockeyRating> jrs = new ArrayList<JockeyRating>();
        try {
            JockeyRatingThreshold thresh = getJockeyDAO().getActiveJockeyRatingThreshold();
            List<JockeyPerformance> perfs = getJockeyDAO().getAllJockeyPerformances();
            for (JockeyPerformance p : perfs) {
                jrs.add(calculatJockeyRating(p, thresh));
            }
        } catch (DAOException e) {
            throw new DatabaseException("Error getting all jockey ratings", e);
        }
        return jrs;
    }

    public List<JockeyRatingThreshold> getAllJockeyRatingThresholds() {
        try {
            return getJockeyDAO().getAllJockeyRatingThresholds();
        } catch (DAOException e) {
            throw new DatabaseException("Error getting all jockey rating thresholds", e);
        }
    }

    public void save(JockeyRatingThreshold threshold) {
        try {
            getJockeyDAO().save(threshold);
        } catch (DAOException e) {
            throw new DatabaseException("Error saving jockey rating threshold", e);
        }
    }

    public void delete(JockeyRatingThreshold threshold) {
        try {
            getJockeyDAO().delete(threshold);
        } catch (DAOException e) {
            throw new DatabaseException("Error deleting jockey rating threshold", e);
        }
    }

    @TransactionBoundary(datasources = Datasource.JOCKEY)
    public int getJockeyPerformanceDatabaseRowCount() {
        try {
            return getJockeyDAO().getJockeyPerformanceRowCount();
        } catch (DAOException e) {
            throw new DatabaseException("Error getting jockey rating database row count", e);
        }
    }

    @TransactionBoundary(datasources = Datasource.JOCKEY)
    public void rebuildJockeyRatingDatabase() {
        try {
            getJockeyDAO().buildDatabase();
        } catch (DAOException e) {
            throw new DatabaseException("Error building jockey rating database", e);
        }
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": Jockey database rebuilt");

    }

    @TransactionBoundary(datasources = Datasource.HISTORY)
    public void rebuildProfileHistoryDatabase() throws DAOException {
        getProfileHistoryRepository().rebuildProfileHistoryDatabase();
    }

    @TransactionBoundary(datasources = {Datasource.TURF_SIRE, Datasource.HISTORY})
    public synchronized void init() {
        if (initialized) {
            throw new DatabaseException("Database already initialized");
        }
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": init()");
        int tCount;
        int daCount;
        int historyCount;
        try {
            getTurfSireDAO().buildDatabase();
            tCount = getTurfSireDAO().getRowCount();
            getDirtArtificialSireDAO().buildDatabase();
            daCount = getDirtArtificialSireDAO().getRowCount();
            // Rebuild only occurs when we think the database doesn't exist
            getProfileHistoryRepository().rebuildProfileHistoryDatabase();
            historyCount = getProfileHistoryRepository().getProfileHistoryCount();
        } catch (DAOException e) {
            throw new DatabaseException("Error initializing database", e);
        }
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": Turf sire database initialized, row count: " + tCount);
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": Dirt/artificial sire database initialized, row count: " + daCount);
        System.out.println("INFO: " + this.getClass().getCanonicalName() + ": Profile history database row count: " + historyCount);
        initialized = true;
    }

    public void setTurfSireDAO(TurfSireDAO dao) {
        this.turfSireDAO = dao;
    }

    public TurfSireDAO getTurfSireDAO() {
        return turfSireDAO;
    }

    public JockeyDAO getJockeyDAO() {
        return jockeyDAO;
    }

    public void setJockeyDAO(JockeyDAO jockeyDAO) {
        this.jockeyDAO = jockeyDAO;
    }

    public DirtArtificialSireDAO getDirtArtificialSireDAO() {
        return dirtArtificialSireDAO;
    }

    public void setDirtArtificialSireDAO(DirtArtificialSireDAO dirtArtificialSireDAO) {
        this.dirtArtificialSireDAO = dirtArtificialSireDAO;
    }

    public void setProfileHistoryRepository(ProfileHistoryRepository profileHistoryRepository) {
        this.profileHistoryRepository = profileHistoryRepository;
    }

    public ProfileHistoryRepository getProfileHistoryRepository() {
        return profileHistoryRepository;
    }
}
